﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Data;
using MySql.Data;
using MySql.Data.MySqlClient;
using System.Resources;
using System.Drawing;
using System.Reflection;

namespace SharedComponents
{
    /// <summary>
    /// Classe che rappresenta un Record.
    /// </summary>
    public class MySqlRecord : Hashtable { }

    /// <summary>
    /// Classe che rappresenta una insieme di Record.
    /// </summary>
    public class MySqlRecordset : List<MySqlRecord> {}

    /// <summary>
    /// Classe statica atta a gestire la connessione ad un DB MySql.
    /// </summary>
    public static class MySqlManager
    {
        public static String Host { get; set; }
        public static String Database { get; set; }
        public static String Username { get; set; }
        public static String Password { get; set; }
        public static MySqlConnection Connection { get; set; }
        
        /// <summary>
        /// Inizializza i parametri predefiniti
        /// </summary>
        public static void Init() 
        {
            Connection = null;
            Host = "82.103.138.122";
            Database = "ourleague";
            Username = "ourleague";
            Password = "0urleagu3";

            /*
             * Questi aggiustamenti permettono di spostare la connessione ad un altro db rispetto quello ufficiale.
             * Una soluzione temporanea per eseguire test in locale.
             */
            /*
            Host = "localhost";
            Username = "root";
            Password = "keluskroot"; 
            */
        }

        /// <summary>
        /// Avvia il tentativo di connessione al DB remoto.
        /// </summary>
        /// <returns>Restituisce true in caso di successo, false altrimenti.</returns>
        public static bool performOpenConnection()
        {
            Connection = openConnection(Host, Username, Password, Database);

            return Connection != null;
        }

        /// <summary>
        /// Chiude la connessione aperto con il DB remoto.
        /// </summary>
        public static void performCloseConnection()
        {
            if (Connection != null)
            {
                Connection.Close();
                Connection = null;
            }
        }

        /// <summary>
        /// Esegue la connessione al DB con le informazioni specificate nei paramentri.
        /// </summary>
        /// <param name="host"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        private static MySqlConnection openConnection(String host, String username, String password, String db)
        {
            performCloseConnection();
            MySqlConnection connection = new MySqlConnection();
            connection.ConnectionString = "Server=" + host + ";Database=" + db + ";Uid=" + username + ";Pwd=" + password + ";";

            try
            {
                connection.Open();

                return connection;
            }
            catch (Exception ex)
            {
                Console.Write("\n" + ex);

                return null;
            }
        }
    }

    public static class Util
    {
        public static Image loadImageFromResources(String name)
        {
            ResourceManager rm = new ResourceManager("Ourleague.Properties.Resources", Assembly.GetExecutingAssembly());
            Image ris = rm.GetObject(name) as Image;
            rm.ReleaseAllResources();

            return ris;
        }
    }

    /// <summary>
    /// Classe statica atta ad offrire diverse funzioni per eseguire specifiche query su un DB MySql.
    /// </summary>
    public static class MySqlQueryHelper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static String quotedObject(Object obj)
        {
            if (obj == null)
                return "NULL";

            if (obj.GetType() == typeof(DateTime))
            {
                DateTime date = (DateTime)obj;
                obj = date.Year + "-" + date.Month + "-" + date.Day;
            }
  
            if (obj.GetType() == typeof(String) ||
                obj.GetType() == typeof(float)
               )
                return "'" + obj.ToString().Replace("'", "''") + "'";
            else
                return obj.ToString();
        }

        /// <summary>
        /// Permette di eseguire operazione di inserimento o modifica di record in una relazione.
        /// </summary>
        /// <param name="connection">Riferimento alla connessione aperta al db remoto.</param>
        /// <param name="relation">Nome della relazione su cui effettuare l'operazione.</param>
        /// <param name="updateCondition">Rappresenta l'espressione per filtrare record da modificare, se ha valore null indica l'operazione di inserimento.</param>
        /// <param name="data">Sequenza di coppie (Colonna -> Valore)</param>
        /// <returns>Restituisce un valore maggiore di 0 in caso di successo (in modalità inserimento il valore corrisponde all'id del nuovo record).
        /// Restituisce il valore 0 in caso di errore.</returns>
        public static int saveQuery(MySqlConnection connection, String relation, String updateCondition, MySqlRecord data)
        {
            //Soluzione temporanea
            relation = relation.ToLower();
            String SQL = "";

            if (updateCondition == null)
            {
                String columns, values;
                columns = values = "";

                foreach (DictionaryEntry entry in data)
                {
                    columns += "`" + entry.Key + "`,";
                    values += quotedObject(entry.Value) + ",";
                }

                SQL = "INSERT INTO `" + relation + "` (" + columns.Remove(columns.Count() - 1) + ") VALUES (" + values.Remove(values.Count() -1) + ");";
            }
            else
            {
                String fields = "";

                foreach (DictionaryEntry entry in data)
                    fields += "`" + entry.Key + "` = " + quotedObject(entry.Value) + ",";

                SQL = "UPDATE `" + relation + "` SET " + fields.Remove(fields.Count() - 1) + " WHERE " + updateCondition + ";";
            }

            try
            {
                Console.Write("\nSQL Query: " + SQL);
                MySqlCommand command = new MySqlCommand(SQL, connection);
                command.ExecuteNonQuery();

                if (updateCondition == null)
                    return (int)command.LastInsertedId;

                return 1;
            }
            catch (Exception ex)
            {
                Console.Write("\n" + ex);

                return 0;
            }
        }

        /// <summary>
        /// Permette di eseguire operazione di cancellazione in una relazione.
        /// </summary>
        /// <param name="connection">Riferimento della connessione aperta al db remoto.</param>
        /// <param name="relation">Nome della relazione su cui effettuare l'operazione.</param>
        /// <param name="conditionParticel">Gli elementi da cancellare possono essere trovati in due modi:
        /// 1. scegliendo un certo attributo e quali valori esso può assumere (ad esempio la colonna della chiave primaria);
        /// 2. specificando una qualsiasi espressione booelana.
        /// 
        /// La funzione si comporta nel caso 1 quando l'array machValues è diverso da null e questo parametro diventa il nome dell'attributo scelto.
        /// Altrimenti è il caso 2 e il parametro è l'espressione condizionale.
        /// </param>
        /// <param name="matchValues">Indica una sequenza di valori atti a cercare quali record cancellare. Si veda il caso 1 descritto per il parametro conditionParticel</param>
        /// <returns>Restituisce true se l'operazione è avvenuta con successo, altrimenti false.</returns>
        public static bool deleteQuery(MySqlConnection connection, String relation, String conditionParticel, Object[] matchValues)
        {
            //Soluzione temporanea
            relation = relation.ToLower();
            String SQL = "";

            if (matchValues != null && matchValues.Count() > 0)
            {
                foreach (Object value in matchValues)
                    SQL += "`" + conditionParticel + "` = " + quotedObject(value) + " OR ";

                SQL = SQL.Remove(SQL.Count() - 3);
            }
            else
                SQL = conditionParticel;

            try
            {
                if (conditionParticel == null)
                    SQL = "TRUNCATE TABLE `" + relation + "`;";
                else
                    SQL = "DELETE FROM `" + relation + "` WHERE " + SQL + ";";

                MySqlCommand command = new MySqlCommand(SQL, connection);
                Console.Write("\nSQL Query: " + SQL);
                command.ExecuteNonQuery();
                
                return true;
            }
            catch (Exception ex)
            {
                Console.Write("\n" + ex);

                return false;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="relation"></param>
        /// <param name="selectedFields"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static String makeSelectQueryString(String relation, String[] selectedFields, String filter)
        {
            //Soluzione temporanea
            relation = relation.ToLower();
            String SQL = "SELECT ";

            if (selectedFields != null && selectedFields.Count() > 0)
            {
                foreach (String field in selectedFields)
                    SQL += field + ",";

                SQL = SQL.Remove(SQL.Count() - 1);
            }
            else
                SQL += "*";

            return SQL + " FROM `" + relation + "`" + (filter != null ? " WHERE " + filter : "") + ";";
        }

        /// <summary>
        /// Permette di eseguire operazione di selezione in una relazione.
        /// </summary>
        /// <param name="connection">Riferimento della connessione aperta al db remoto.</param>
        /// <param name="relation">Nome della relazione su cui effettuare l'operazione.</param>
        /// <param name="selectedFields">Indica una lista di campi che si selezionare (se null interesserà tutti i campi della relazione).</param>
        /// <param name="filter">Indica una espressione per filtrare ulteriormente la selezione (può essere null).</param>
        /// <returns>Restituisce il risultato della selezione come una lista di array associativi di chiave corrispondente al nome dell'attributo,
        /// altrimenti è null in caso di errore.</returns>
        public static MySqlRecordset selectQuery(MySqlConnection connection, String relation, String[] selectedFields, String filter)
        {
            //Soluzione temporanea
            relation = relation.ToLower();
            MySqlRecordset result = null;
            String SQL = makeSelectQueryString(relation, selectedFields, filter);

            try
            {
                MySqlCommand command = new MySqlCommand(SQL, connection);
                Console.Write("\nSQL Query: " + SQL);
                MySqlDataReader queryResult = command.ExecuteReader();

                result = new MySqlRecordset();
                while (queryResult.Read())
                {
                    result.Add(new MySqlRecord());
                    for(int i = 0; i< queryResult.FieldCount; i++)
                        result[result.Count - 1].Add(queryResult.GetName(i), queryResult.GetValue(i));
                }

                queryResult.Close();
            }
            catch (Exception ex)
            {
                Console.Write("\n" + ex);
            }

            return result;
        }
    }
}
